package su.msk.jet.team01.chatik.server;

import java.util.HashMap;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import su.msk.jet.team01.chatik.server.commands.ByeCommand;
import su.msk.jet.team01.chatik.server.commands.ChangeNameCommand;
import su.msk.jet.team01.chatik.server.commands.ChangeRoomCommand;
import su.msk.jet.team01.chatik.server.commands.Command;
import su.msk.jet.team01.chatik.server.commands.HelpCommand;
import su.msk.jet.team01.chatik.server.commands.ListRoomsCommand;
import su.msk.jet.team01.chatik.server.commands.authorized.GetHistoryCommand;
import su.msk.jet.team01.chatik.server.commands.authorized.SendMessageCommand;
import su.msk.jet.team01.chatik.server.commands.authorized.SendPrivateCommand;
import su.msk.jet.team01.chatik.server.data.Client;
import su.msk.jet.team01.chatik.server.exception.CommandNotFoundException;
import su.msk.jet.team01.chatik.server.exception.InvalidArgsCountException;
import su.msk.jet.team01.chatik.server.exception.InvalidArgumentException;

public class CommandProcessor {

	private static final String EXCEPTION_MSG_COMMAND_NOT_FOUND = "Command %s not found";
	private static Map<String, Command> commands = new HashMap<String, Command>();

	private static Logger logger = LoggerFactory
			.getLogger(CommandProcessor.class);

	static {
		addCommand(new ListRoomsCommand());
		addCommand(new ChangeRoomCommand());
		addCommand(new ChangeNameCommand());
		addCommand(new SendPrivateCommand());
		addCommand(new SendMessageCommand());
		addCommand(new GetHistoryCommand());
		addCommand(new HelpCommand());
		addCommand(new ByeCommand());
	}

	private static class Holder {
		public static final CommandProcessor instance = new CommandProcessor();
	}
	
	public static CommandProcessor getInstance() {
		return Holder.instance;
	}
	
	private static void addCommand(final Command command) {
		synchronized(commands) {
			commands.put(command.getName(), command);
		}
	}

	public void process(final Client client, final String input) {
		Command command;
		String commandString;

		if (input.isEmpty()) {
			logger.warn("Cheater detected! Client " + client
					+ " sent empty message");
			return;
		}

		logger.info(client + ": " + input);
		commandString = input.replaceAll("(^[^/].*|(/|[^\\S+][\\s\\S]*))", "");
		try {
			command = getCommand(commandString.isEmpty() ? "msg"
					: commandString);
			command.execute(client, extractArgs(input, command.getMaxArgsCount()));
		} catch (CommandNotFoundException e) {
			logger.info(e.getMessage());
			client.send("Unknown command");
		} catch (InvalidArgsCountException e) {
			logger.info(e.getMessage());
			client.send("Bad syntax");
		} catch (InvalidArgumentException e) {
			logger.info(e.getMessage());
			client.send("Bad syntax");
		}
	}

	private String[] extractArgs(final String input, final int argsCount) {
		String argsString = input.replaceAll("(^/\\S+\\s*|\\s+$)", "");
		String[] result = argsString.split("\\s+", argsCount);
		if(result[0].isEmpty()) {
			return new String[] {};
		}
		return result;
	}

	private Command getCommand(final String name)
			throws CommandNotFoundException {
		Command command;

		synchronized(commands) {
			command = commands.get(name);
		}
		
		if (command == null) {
			throw new CommandNotFoundException(EXCEPTION_MSG_COMMAND_NOT_FOUND,
					name);
		}

		return command;
	}
}
